﻿using System;
using System.Collections.Generic;
using Brouteforce.Characters;
using Brouteforce.Map;
using Brouteforce.UserInterface.Abstracts;
using Brouteforce.utilities;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Brouteforce.NewManagers;

namespace Brouteforce.Screens.PlayingScreen.Panels {
    public class GamePanel : AbstractPanel {
        private TileMap _myMap;
        private const int SquaresAcross = 20;
        private const int SquaresDown = 39;
        private const int BaseOffsetX = -32;
        private const int BaseOffsetY = -64;
        private const float HeightRowDepthMod = 0.00001f;
        private static SpriteFont _pericles6;
        private static Texture2D _highlightTexture;
        private static Texture2D _selectionTexture;
        public Character Selected;
        protected int UpdateLoopIndex = 0;
        private static Texture2D SlopeMap;
        private static Texture2D MouseMap;

        public GamePanel()
            : base("Game Panel") {

        }

        public override void Update(GameTime gameTime) {
            
            KeyboardState ks = Keyboard.GetState();
            if (ks.IsKeyDown(Keys.Up)) Camera.Move(new Vector2(0, -5));
            if (ks.IsKeyDown(Keys.Down)) Camera.Move(new Vector2(0, 5));
            if (ks.IsKeyDown(Keys.Left)) Camera.Move(new Vector2(-5, 0));
            if (ks.IsKeyDown(Keys.Right)) Camera.Move(new Vector2(5, 0));
            if (InputManager.Instance.MouseClicked.IsChangedSince(UpdateLoopIndex) && InputManager.Instance.IsMouseClickedOn(Bounds)) {
                var chars = CharactersInCellUnderMouse;
                if (chars.Count == 0) {
                    Selected.MoveTo(WorldLocationOfCellUnderMouse);
                } else {
                    //Console.WriteLine("there is a new character selected!");
                    if (Selected != null)
                        Selected.Selected = false;

                    Selected = chars[0];
                    Selected.Selected = true;
                }
            }
            if (InputManager.Instance.MouseClicked.IsChangedSince(UpdateLoopIndex)&& InputManager.Instance.IsMouseClickedOn(Bounds)) {
                Vector2 pos = new Vector2(InputManager.Instance.CurMouseX, InputManager.Instance.CurMouseY);
                pos = Camera.ScreenToWorld(pos);
                Point mcelp = _myMap.WorldToMapCell(pos);
                if (Selected != null) {
                    _myMap.HighlightShootRange(mcelp.X, mcelp.Y, Color.Red, 5, Selected);
                }
            }
            UpdateLoopIndex = BFGame.LoopIndex;
        }

        public override void Draw(GameTime gameTime, SpriteBatch spriteBatch) {

            Vector2 firstSquare = new Vector2(Camera.Location.X / Tile.TileStepX, Camera.Location.Y / Tile.TileStepY);
            int firstX = (int)firstSquare.X;
            int firstY = (int)firstSquare.Y;

            Vector2 squareOffset = new Vector2(Camera.Location.X % Tile.TileStepX, Camera.Location.Y % Tile.TileStepY);
            int offsetX = (int)squareOffset.X;
            int offsetY = (int)squareOffset.Y;

            float maxdepth = ((_myMap.MapWidth + 1) * ((_myMap.MapHeight + 1) * Tile.TileWidth)) / 10;
            float depthOffset;


            for (int y = 0; y < SquaresDown; y++) {
                int rowOffset = 0;
                if ((firstY + y) % 2 == 1)
                    rowOffset = Tile.OddRowXOffset;

                for (int x = 0; x < SquaresAcross; x++) {
                    int mapx = (firstX + x);
                    int mapy = (firstY + y);
                    depthOffset = 0.7f - ((mapx + (mapy * Tile.TileWidth)) / maxdepth);

                    if ((mapx >= _myMap.MapWidth) || (mapy >= _myMap.MapHeight))
                        continue;

                    //draw base tiles
                    foreach (int tileID in _myMap.Rows[mapy].Columns[mapx].BaseTiles) {
                        BFGame.SpriteBatch.Draw(
                            Tile.TileSetTexture,
                            Camera.WorldToScreen(
                                new Vector2((mapx * Tile.TileStepX) + rowOffset, mapy * Tile.TileStepY)),
                            Tile.GetSourceRectangle(tileID),
                            Color.White,
                            0.0f,
                            Vector2.Zero,
                            1.0f,
                            SpriteEffects.None,
                            1.0f);
                    }
                    int heightRow = 0;

                    //draw hight tiles
                    foreach (int tileID in _myMap.Rows[mapy].Columns[mapx].HeightTiles) {
                        Texture2D tx;
                        Rectangle rect;

                        if (tileID < 0)
                        {
                            var temp = Tile.Textures[-tileID];
                            tx = temp.Key;
                            rect = temp.Value;
                        } else {
                            tx = Tile.TileSetTexture;
                            rect = Tile.GetSourceRectangle(tileID);
                        }

                        BFGame.SpriteBatch.Draw(
                            tx,
                            Camera.WorldToScreen(
                                new Vector2(
                                    (mapx * Tile.TileStepX) + rowOffset,
                                    mapy * Tile.TileStepY - (heightRow * Tile.HeightTileOffset))),
                            rect,
                            Color.White,
                            0.0f,
                            Vector2.Zero,
                            1.0f,
                            SpriteEffects.None,
                            depthOffset - ((float)heightRow * HeightRowDepthMod));
                        heightRow++;
                    }

                    //draw topper tiles
                    foreach (int tileID in _myMap.Rows[y + firstY].Columns[x + firstX].TopperTiles) {
                        BFGame.SpriteBatch.Draw(
                            Tile.TileSetTexture,
                            Camera.WorldToScreen(
                                new Vector2((mapx * Tile.TileStepX) + rowOffset, mapy * Tile.TileStepY)),
                            Tile.GetSourceRectangle(tileID),
                            Color.White,
                            0.0f,
                            Vector2.Zero,
                            1.0f,
                            SpriteEffects.None,
                            depthOffset - ((float)heightRow * HeightRowDepthMod));
                    }

                    //update vlads row depth
                    _myMap.Rows[mapy].Columns[mapx].CharactersInCell.ForEach(vlad => vlad.DrawDepth = depthOffset - (float)(heightRow + 2) * HeightRowDepthMod);

                    //put highlight on the cell if it need to be there
                    if (_myMap.Rows[mapy].Columns[mapx].Highlighted != null) {
                        DrawHighlightAt(new Point(mapx, mapy), _highlightTexture);
                    }

                    //draw position of tiles for debug reason
                    BFGame.SpriteBatch.DrawString(_pericles6, (x + firstX).ToString() + ", " + (y + firstY).ToString(),
                        new Vector2((x * Tile.TileStepX) - offsetX + rowOffset + BaseOffsetX + 24,
                            (y * Tile.TileStepY) - offsetY + BaseOffsetY + 48), Color.White, 0f, Vector2.Zero,
                            1.0f, SpriteEffects.None, 0.0f);
                }

                Draws.DrawBorder(Bounds, new Color(91, 91, 91), 2);

            }

            //draw highlight under mouse
            Vector2 hilightLoc = Camera.ScreenToWorld(new Vector2(Mouse.GetState().X, Mouse.GetState().Y));
            Point hilightPoint = _myMap.WorldToMapCell(new Point((int)hilightLoc.X, (int)hilightLoc.Y));
            DrawHighlightAt(hilightPoint, _highlightTexture);

            BFGame.SpriteBatch.DrawString(_pericles6, "mouse world over: " + hilightLoc, new Vector2(400, 0), Color.White);

            //draw characters - should not be all charecters - only the ones in the visible area of the map!! (FIX IT LATER)
            foreach (var c in _myMap.CharactersInMap) {

                int charY = -_myMap.GetOverallHeight(c.PositionInWorld);
                BFGame.SpriteBatch.Draw(c.Texture,
                    Camera.WorldToScreen(c.PositionInWorld) + c.DrawOffset + new Vector2(-4, charY + 2),
                    c.Texture.Bounds, Color.White,
                    0, Vector2.Zero, 0.6f, SpriteEffects.None, c.DrawDepth);

                //draw selection rectengle
                if (c.Selected) {
                    Point selectionPoint = _myMap.WorldToMapCell(new Point((int)c.PositionInWorld.X, (int)c.PositionInWorld.Y));
                    BFGame.SpriteBatch.DrawString(_pericles6, "position: " + selectionPoint, new Vector2(200, 0), Color.White);
                    DrawHighlightAt(selectionPoint, _selectionTexture);
                }
            }

            //draw events
            foreach (var c in _myMap.CharactersInMap) {
                int charY = -_myMap.GetOverallHeight(c.PositionInWorld);
                BFGame.SpriteBatch.Draw(c.Texture,
                    Camera.WorldToScreen(c.PositionInWorld) + c.DrawOffset + new Vector2(-4, charY + 2),
                    c.Texture.Bounds, Color.White,
                    0, Vector2.Zero, 0.6f, SpriteEffects.None, c.DrawDepth);

                //draw selection rectengle
                if (c.Selected) {
                    Point selectionPoint = _myMap.WorldToMapCell(new Point((int)c.PositionInWorld.X, (int)c.PositionInWorld.Y));
                    BFGame.SpriteBatch.DrawString(_pericles6, "position: " + selectionPoint, new Vector2(200, 0), Color.White);
                    DrawHighlightAt(selectionPoint, _selectionTexture);
                }
            }
        }

        public static void LoadContent(ContentManager content) {
            _highlightTexture = content.Load<Texture2D>(@"Textures\TileSets\hilight");
            _selectionTexture = content.Load<Texture2D>(@"Textures\TileSets\selection");

            Tile.TileSetTexture = content.Load<Texture2D>(@"Textures\TileSets\part4_tileset");
            Tile.CastleTexture = content.Load<Texture2D>(@"Textures\Buildings\tower2");

            _pericles6 = content.Load<SpriteFont>(@"Fonts\Pericles6");

            MouseMap = content.Load<Texture2D>(@"Textures\TileSets\mousemap");
            SlopeMap = content.Load<Texture2D>(@"Textures\TileSets\part9_slopemaps");

            Tile.CutContent();
            Character.LoadContent(content);

            
        }

        public override void AfterLayout() {
            _myMap = new TileMap(MouseMap, SlopeMap);

            Camera.ViewWidth = BFGame.Graphics.PreferredBackBufferWidth;
            Camera.ViewHeight = BFGame.Graphics.PreferredBackBufferHeight;
            Camera.WorldWidth = ((_myMap.MapWidth - 2) * Tile.TileStepX);
            Camera.WorldHeight = ((_myMap.MapHeight - 2) * Tile.TileStepY);
            Camera.DisplayOffset = new Vector2(BaseOffsetX, BaseOffsetY);


            Selected = new Defender(new Resources(), false, this, WorldLocationOfMapCell(new Point(6, 9)));
            Selected.Selected = true;
            InsertCharacterToMap(Selected);

            InsertCharacterToMap(new Defender(new Resources(), true, this, WorldLocationOfMapCell(new Point(8, 5))));
            InsertCharacterToMap(new Bomber(new Resources(), false, this, WorldLocationOfMapCell(new Point(3, 4))));
            InsertCharacterToMap(new Archer(new Resources(), true, this, WorldLocationOfMapCell(new Point(7, 6))));
            InsertCharacterToMap(new Bomber(new Resources(), false, this, WorldLocationOfMapCell(new Point(10, 9))));
        }

        public Vector2 WorldLocationOfCellUnderMouse {
            get {
                Vector2 hilightLoc = Camera.ScreenToWorld(new Vector2(Mouse.GetState().X, Mouse.GetState().Y));
                Point p = _myMap.WorldToMapCell(new Point((int)hilightLoc.X, (int)hilightLoc.Y));

                Console.WriteLine("World Cell Under Mouse: " + p);

                //Vector2 hilightLoc = Camera.ScreenToWorld(new Vector2(Mouse.GetState().X, Mouse.GetState().Y));
                //Point p = myMap.WorldToMapCell(new Point((int)hilightLoc.X, (int)hilightLoc.Y));
                return WorldLocationOfMapCell(p);
            }
        }

        private static Vector2 WorldLocationOfMapCell(Point p)
        {
            int rowOffset = 0;
            if ((p.Y)%2 == 1)
                rowOffset = Tile.OddRowXOffset;
            var ret = new Vector2((p.X*Tile.TileStepX) + rowOffset + 32, (p.Y + 2)*Tile.TileStepY + 8);
            //Console.WriteLine("World Position of Cell Is: " + ret);
            return ret;
        }


        public List<Character> CharactersInCellUnderMouse {
            get {
                Vector2 hilightLoc = Camera.ScreenToWorld(new Vector2(Mouse.GetState().X, Mouse.GetState().Y));
                Point p = _myMap.WorldToMapCell(new Point((int)hilightLoc.X, (int)hilightLoc.Y));
                return _myMap.Rows[p.Y].Columns[p.X].CharactersInCell;
            }
        }


        private void DrawHighlightAt(Point mapCell, Texture2D hlt) {
            int hilightrowOffset = 0;
            if ((mapCell.Y) % 2 == 1)
                hilightrowOffset = Tile.OddRowXOffset;

            BFGame.SpriteBatch.Draw(
                            hlt,
                            Camera.WorldToScreen(
                            new Vector2(
                                (mapCell.X * Tile.TileStepX) + hilightrowOffset,
                                (mapCell.Y + 2) * Tile.TileStepY)),
                            new Rectangle(0, 0, 64, 32),
                            Color.White * 0.4f,
                            0.0f,
                            Vector2.Zero,
                            1.0f,
                            SpriteEffects.None,
                            0.0f);
        }


        public void InsertCharacterToMap(Character c) {
            c.UpdateTileLocation(_myMap);
            _myMap.CharactersInMap.Add(c);
        }

        public void UpdateCharacterCellLocation(Character c) {
            c.UpdateTileLocation(_myMap);
        }
    }
}
